import { Config, Inject, Provide } from '@midwayjs/decorator';
import { BaseService, CoolCommException } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { In, Repository } from 'typeorm';
import { BizOrderListEntity } from '../entity/list';
import { BizOrderDetailEntity } from '../entity/detail';
import { BizOrderPayListEntity } from '../entity/payList';
import { BizOrderProofListEntity } from '../entity/proofList';
import { BizWorkerWorkDatesEntity } from '../../bizWorker/entity/workDates';
import { BizWorkerListEntity } from '../../bizWorker/entity/list';
import { BizCustomerListEntity } from '../../bizCustomer/entity/list';
import { BizOrderToPayListEntity } from '../entity/toPayList';
import { PluginService } from '../../plugin/service/info';
import { Context } from '@midwayjs/koa';
import Decimal from 'decimal.js';
import { formatDate } from '../../../comm/date';

/**
 * 订单列表服务
 */
@Provide()
export class BizOrderListService extends BaseService {
  @Inject()
  ctx: Context;

  @Config('module.bizOrder.toPayOrderTimmers')
  toPayOrderTimmers: any;

  @InjectEntityModel(BizOrderListEntity)
  bizOrderListEntity: Repository<BizOrderListEntity>;
  @InjectEntityModel(BizOrderDetailEntity)
  bizOrderDetailEntity: Repository<BizOrderDetailEntity>;
  @InjectEntityModel(BizOrderPayListEntity)
  bizOrderPayListEntity: Repository<BizOrderPayListEntity>;
  @InjectEntityModel(BizOrderProofListEntity)
  bizOrderProofListEntity: Repository<BizOrderProofListEntity>;
  @InjectEntityModel(BizOrderToPayListEntity)
  bizOrderToPayListEntity: Repository<BizOrderToPayListEntity>;
  @InjectEntityModel(BizWorkerWorkDatesEntity)
  bizWorkerWorkDatesEntity: Repository<BizWorkerWorkDatesEntity>;
  @InjectEntityModel(BizWorkerListEntity)
  bizWorkerListEntity: Repository<BizWorkerListEntity>;
  @InjectEntityModel(BizCustomerListEntity)
  bizCustomerListEntity: Repository<BizCustomerListEntity>;

  // 注入插件服务
  @Inject()
  pluginService: PluginService;

  async page(
    query: any,
    option: any,
    connectionName?: any
  ): Promise<{
    list: any;
    pagination: { page: number; size: number; total: number };
  }> {
    const { page, size, sort, order, workerId } = query;
    const { fieldEq, keyWordLikeFields } = option;

    const whereStrs = [];
    if (workerId) {
      whereStrs.push(`workerIds REGEXP '(^|,)${workerId}(,|$)'`);
    }
    fieldEq.forEach((key: any) => {
      const queryStr = query[key];

      if (queryStr !== undefined) {
        if (key === 'status') {
          const statusList = queryStr.toString().split(',');
          let statusSql: string[] = [];
          statusList.forEach((status: string) => {
            statusSql.push(`OL.status = ${status}`);
          });
          whereStrs.push(`(${statusSql.join(' || ')})`);
        } else {
          whereStrs.push(`OL.${key} = '${queryStr}'`);
        }
      }
    });
    keyWordLikeFields.forEach((key: any) => {
      const { keyWord } = query;
      keyWord !== undefined && whereStrs.push(`${key} like '%${keyWord}%'`);
    });

    const [[{ total }], orderList] = await Promise.all([
      this.nativeQuery(`
        SELECT
          COUNT(*) AS total
        FROM
          biz_order_list OL
        ${whereStrs.length ? `WHERE ${whereStrs.join(' && ')}` : ''};
      `),
      this.nativeQuery(`
        SELECT
          OL.*,
          DATE_FORMAT(OL.startDate, '%Y-%m-%d') as startDate,
          DATE_FORMAT(OL.endDate, '%Y-%m-%d') as endDate,
          OD.content AS detail
        FROM
          biz_order_list OL
        LEFT JOIN
          biz_order_detail OD ON OL.id = OD.orderId
        ${whereStrs.length ? `WHERE ${whereStrs.join(' && ')}` : ''}
        ORDER BY ${order ? order : 'OL.id'} ${
        sort ? sort.toUpperCase() : 'DESC'
      }
        LIMIT ${(page - 1) * size},${size};
      `),
    ]);

    return {
      list: orderList,
      pagination: {
        page,
        size,
        total: Number(total),
      },
    };
  }

  async info(id: any, infoIgnoreProperty?: string[]): Promise<any> {
    const list = await this.nativeQuery(`
      SELECT
        OL.*,
        DATE_FORMAT(OL.startDate, '%Y-%m-%d') as startDate,
        DATE_FORMAT(OL.endDate, '%Y-%m-%d') as endDate,
        OD.content AS detail
      FROM
        biz_order_list OL
      LEFT JOIN
        biz_order_detail OD ON OL.id = OD.orderId
      WHERE
        OL.id = ${id};
    `);
    const order = list[0];

    const proofInfo = await this.bizOrderProofListEntity.findOneBy({
      orderId: order.id,
    });

    order['proof'] = proofInfo?.photo || '';

    return order;
  }

  // 创建预约订单
  async placeOrder(
    userId: number,
    startDate: string,
    endDate: string,
    priceDeposit: number,
    detail: string
  ) {
    const detailObj = JSON.parse(detail);

    // 生成订单
    const workTypes = [];
    const workerIds = [];
    detailObj.order.forEach((work: any) => {
      workTypes.push(work.workType);
      work.workers.forEach((worker: any) => {
        workerIds.push(worker.id);
      });
    });
    const orderNo = await this.pluginService.invoke('pay-wx', 'createOrderNum');
    const {
      identifiers: [{ id }],
    } = await this.bizOrderListEntity.insert({
      orderNo,
      status: 2,
      userId,
      workTypes: workTypes.join(','),
      workerIds: workerIds.join(','),
      startDate,
      endDate,
      priceDeposit,
    });

    // 保存订单详情
    await this.bizOrderDetailEntity.insert({
      orderId: id,
      content: detail,
    });

    // 添加师傅工期
    const newWorkDates: any = [];
    detailObj.order.forEach((work: any) => {
      work.workers.forEach((worker: any) => {
        newWorkDates.push({
          orderId: id,
          workerId: worker.id,
          startDate,
          endDate,
        });
      });
    });
    await this.bizWorkerWorkDatesEntity.insert(newWorkDates);

    orderNo && id && this.sendOrderSMS(orderNo); // 这个可以不异步

    return {
      id,
    };
  }

  // 生成微信支付订单 需要前端防抖
  async placePayOrder(orderId: number, userId: number) {
    const [orderData, userData] = await Promise.all([
      this.bizOrderListEntity.findOneBy({ id: orderId }),
      this.bizCustomerListEntity.findOneBy({ id: userId }),
    ]);
    const { status, orderNo } = orderData;
    const { wechatOpenId } = userData;

    const 订单状态_定金待支付 = status === 2;
    const 订单状态_尾款待支付 = status === 5;

    if (订单状态_定金待支付 || 订单状态_尾款待支付) {
      const wxPayConfig = this.ctx.app.getConfig('getWxPayConfig')();
      const wxPay = await this.pluginService.invoke(
        'pay-wx',
        'getInstance',
        wxPayConfig
      );

      const { description, total } = this.getPayInfoByStatus(orderData);
      const { notify_url } = wxPayConfig;
      const orderType = 订单状态_定金待支付 ? 0 : 1;
      const out_trade_no = `${orderNo}-${orderType}`; // 微信的订单号组成： [orderNo-系统订单号]-[orderType-订单类型  0-定金类型 1-尾款类型]
      const payParma = {
        description,
        out_trade_no,
        notify_url,
        amount: {
          total: new Decimal(total).mul(new Decimal(100)).toNumber(), // Number(0.01) * 100,
        },
        payer: {
          openid: wechatOpenId,
        },
        scene_info: {
          payer_client_ip: this.ctx.request.ip,
        },
      };
      const payData = await wxPay.transactions_jsapi(payParma);

      if (payData.error) {
        throw new CoolCommException(payData.error);
      } else {
        await this.toPayOrderCheckout({
          tOrderNo: out_trade_no,
          userId,
          orderNo,
          total,
          status,
        });
        return {
          payData: payData.data,
        };
      }
    } else {
      throw new CoolCommException('当前订单状态不可生成支付订单');
    }
  }

  // 将待支付订单入库保存
  async toPayOrderCheckout(p: any) {
    const { tOrderNo, userId, orderNo, total, status } = p;

    const 订单状态_定金待支付 = status === 2;
    const 订单状态_尾款待支付 = status === 5;

    const toPayData = await this.bizOrderToPayListEntity.findOneBy({
      tOrderNo,
    });
    if (!toPayData) {
      await this.bizOrderToPayListEntity.insert({
        tOrderNo,
        userId: userId,
        orderType: 0,
        orderNo,
        tType: 0,
        price: total,
        notes: 订单状态_定金待支付 ? '订单定金 微信支付' : '订单尾款 微信支付',
      });
    }
  }

  // 微信支付回调通知
  async wxPayNotify(param: any) {
    const {
      resource: { ciphertext, associated_data, nonce },
    } = param;

    const wxPayConfig = this.ctx.app.getConfig('getWxPayConfig')();
    const wxPay = await this.pluginService.invoke(
      'pay-wx',
      'getInstance',
      wxPayConfig
    );

    const { trade_state, out_trade_no } = await wxPay.decipher_gcm(
      ciphertext,
      associated_data,
      nonce,
      wxPayConfig.key
    );

    if (trade_state === 'SUCCESS') {
      await this.updatePayList(out_trade_no);
    } else {
      console.log('微信支付回调异常结果', param);
    }
  }

  // 更新支付订单列表并修改订单状态
  async updatePayList(tOrderNo: string) {
    const toPayOrderInfo = await this.bizOrderToPayListEntity.findOneBy({
      tOrderNo,
    });

    if (toPayOrderInfo) {
      const oldId = toPayOrderInfo.id;

      const payOrderInfo: any = toPayOrderInfo; // 只是为了换一个名
      delete payOrderInfo.id;
      delete payOrderInfo.createTime;
      delete payOrderInfo.updateTime;
      payOrderInfo.pricePayDate = new Date();

      const orderData = await this.bizOrderListEntity.findOneBy({
        orderNo: payOrderInfo.orderNo,
      });
      const { status, id, workerIds } = orderData;
      const 订单状态_定金待支付 = status === 2;
      const 订单状态_尾款待支付 = status === 5;
      const 支付完成后的订单目标状态 = 订单状态_定金待支付 ? 3 : 1; // 支付完成后需要变更为的订单状态 2-定金待支付 => 3-施工中 | 5-尾款待支付 => 1-已完成
      const newStatusObj: any = { status: 支付完成后的订单目标状态 };
      if (订单状态_定金待支付) {
        // 修改定金支付时间
        newStatusObj.priceDepositPayDate = new Date();
      } else if (订单状态_尾款待支付) {
        // 修改尾款支付时间
        newStatusObj.priceAllResiduePayDate = new Date();
      }

      await Promise.all([
        this.bizOrderToPayListEntity.delete(oldId), // 删除三方待支付订单表
        this.bizOrderPayListEntity.insert(payOrderInfo), // 添加三方已支付订单表
        this.bizOrderListEntity.update(id, newStatusObj), // 修改订单状态
      ]);

      if (支付完成后的订单目标状态 === 1) {
        // 无需同步执行
        this.updateWorkersOrderNums(workerIds);
      }
    }
  }

  // 更新工人完成订单数量
  async updateWorkersOrderNums(workerIds_: string) {
    const workerIds = workerIds_.split(',').map((v: string) => Number(v));
    const workerDatas = await this.bizWorkerListEntity.findBy({
      id: In(workerIds),
    });
    const whenStrList = workerDatas.map(workerInfo => {
      const orderNums = workerInfo.orderNums + 1;
      return `WHEN id = ${workerInfo.id} THEN ${orderNums}`;
    });
    const whenStr = whenStrList.join(' ');

    await this.nativeQuery(`
      UPDATE biz_worker_list
      SET
        orderNums = CASE
          ${whenStr}
            ELSE orderNums
        END
      WHERE
        id IN(${workerIds_});
    `);
  }

  // 取消订单 只有定金未支付时可以取消订单，需要同步取消微信支付的订单
  async cancelOrder(tOrderNo: string, orderNo: string, notes: string) {
    const [orderData, toPayData] = await Promise.all([
      this.bizOrderListEntity.findOneBy({ orderNo }),
      this.bizOrderToPayListEntity.findOneBy({
        tOrderNo,
      }),
    ]);

    const workDates = await this.bizWorkerWorkDatesEntity.findBy({
      orderId: orderData.id,
      workerId: In(orderData.workerIds.split(',')),
    });

    switch (orderData.status) {
      case 0: {
        throw new CoolCommException('当前订单已取消，不可再次取消！');
      }
      case 1: {
        throw new CoolCommException('当前订单已完成，不可取消！');
      }
      case 2: {
        await Promise.all([
          this.cancelWxPayOrder(tOrderNo), // 取消微信支付订单
          this.bizWorkerWorkDatesEntity.delete(workDates.map(v => v.id)), // 删除工人对应订单对应工期
          toPayData?.id && this.bizOrderToPayListEntity.delete(toPayData.id), // 删除待支付三方订单
          this.bizOrderListEntity.update({ orderNo }, { status: 0, notes }), // 更新订单状态
        ]);
        break;
      }
      case 3: {
        throw new CoolCommException('当前订单正在进行中，不可取消！');
      }
      case 4: {
        throw new CoolCommException('当前订单已完工，不可取消！');
      }
      case 5: {
        throw new CoolCommException('当前订单已完工，不可取消！');
      }
      default: {
        throw new CoolCommException(`订单状态异常 status:${orderData.status}`);
      }
    }
  }

  // 取消微信支付订单
  async cancelWxPayOrder(wxOrderNo: string) {
    const wxPayConfig = this.ctx.app.getConfig('getWxPayConfig')();
    const wxPay = await this.pluginService.invoke(
      'pay-wx',
      'getInstance',
      wxPayConfig
    );

    await wxPay.close(wxOrderNo);
  }

  // 用户主动取消订单
  async activeCancelOrder(orderNo: string, notes: string, orderType: number) {
    const tOrderNo = `${orderNo}-${orderType}`;

    await this.cancelOrder(tOrderNo, orderNo, notes);
  }

  // 通过status获取对应应该支付的金额
  getPayInfoByStatus(data: any) {
    const { status } = data;
    if (status === 2) {
      return {
        description: `订单定金`,
        total: data.priceDeposit,
      };
    }
    if (status === 5) {
      return {
        description: `订单总尾款`,
        total: data.priceAllResidue,
      };
    }

    return;
  }

  // 主动获取三方支付订单状态并刷新订单状态
  async activeUpdateTOrderStatus(
    orderNo: string, // 订单号
    orderType: number // 订单类型 定金/尾款
  ) {
    if (!orderNo) {
      throw new CoolCommException('参数错误');
    }
    const orderData = await this.bizOrderListEntity.findOneBy({ orderNo });
    if (!orderData) {
      throw new CoolCommException('订单不存在！');
    }
    if (orderData.status !== 2 && orderData.status !== 5) {
      // 订单不在支付中，统一返回ok
      return {
        status: 1,
      };
    }

    const tOrderNo = `${orderNo}-${orderType}`;
    let payOrderData = await this.bizOrderToPayListEntity.findOneBy({
      orderNo,
    });

    if (!payOrderData) {
      // 如果不存在待支付订单，那么可能是已经支付完成，订单删掉转存到了支付订单中
      payOrderData = await this.bizOrderPayListEntity.findOneBy({ orderNo });
    }

    const { tType } = payOrderData;
    if (tType === 0) {
      const payRes = await this.getWxPayOrder(tOrderNo);
      if (payRes.status === 200 && payRes.data.trade_state === 'SUCCESS') {
        // 订单支付ok了，去刷新状态
        await this.updatePayList(tOrderNo);
        return {
          status: 1,
        };
      } else {
        // 订单仍未支付成功
        return {
          status: 0,
        };
      }
    } else {
      // 支付宝等 暂无
      return {
        status: 0,
      };
    }
  }

  // 检查微信支付订单状态
  async getWxPayOrder(wxOrderNo: string) {
    const wxPayConfig = this.ctx.app.getConfig('getWxPayConfig')();
    const wxPay = await this.pluginService.invoke(
      'pay-wx',
      'getInstance',
      wxPayConfig
    );

    const wxOrder = await wxPay.query({ out_trade_no: wxOrderNo });

    return wxOrder;
  }

  // 上传凭证 修改订单状态
  async upLoadProof(p: any) {
    const { orderId, workerId, priceSelfWorkAll, photo, notes } = p;
    const [oldProof, orderInfo] = await Promise.all([
      this.bizOrderProofListEntity.findOneBy({
        orderId,
        workerId,
      }),
      this.bizOrderListEntity.findOneBy({ id: orderId }),
    ]);

    if (orderInfo.status !== 3) {
      throw new CoolCommException('当前订单不可上传凭证！');
    }

    const addProof = async () => {
      const {
        identifiers: [{ id }],
      } = await this.bizOrderProofListEntity.insert({
        orderId,
        workerId,
        priceSelfWorkAll,
        photo,
        notes,
      });

      return id;
    };

    let resId = 0;
    if (oldProof) {
      const [id] = await Promise.all([
        addProof(),
        this.bizOrderProofListEntity.delete(oldProof.id),
      ]);
      resId = id;
    } else {
      resId = await addProof();
    }

    await this.refreshOrderStatus(orderId);

    return {
      proofId: resId,
    };
  }

  // 刷新订单状态到审核中
  async refreshOrderStatus(orderId: number) {
    const [allProof, orderInfo, orderDetail] = await Promise.all([
      this.bizOrderProofListEntity.findBy({ orderId }),
      this.bizOrderListEntity.findOneBy({ id: orderId }),
      this.bizOrderDetailEntity.findOneBy({ orderId }),
    ]);

    const content = orderDetail.content as any;

    const workers = [];
    content.order.forEach((order: any) => {
      order.workers.forEach((worker: any) => {
        workers.push(worker);
      });
    });

    if (allProof.length === workers.length) {
      // 记录和工人数量一致 => 修改订单状态为审核中以及各种金额的结算

      let priceAll = new Decimal(0); // 订单施工总价 工人上传凭证时填入
      allProof.forEach((p: any) => {
        priceAll = priceAll.add(new Decimal(p.priceSelfWorkAll));
      });
      const priceTip = priceAll.mul(new Decimal(0.03)); // 平台管理费 = 订单施工总价 * 3%
      const priceWorkerAll = priceAll.sub(priceTip); // 师傅收入总额 (订单施工总价 - 平台管理费)
      const priceDeposit = new Decimal(orderInfo.priceDeposit); // 总定金
      const priceAllResidue = priceAll.sub(priceDeposit); // 订单尾款 = 订单施工总价 - 总定金
      await this.bizOrderListEntity.update(orderInfo.id, {
        status: 4, // 审核中
        priceAll: priceAll.toNumber(),
        priceTip: priceTip.toNumber(),
        priceWorkerAll: priceWorkerAll.toNumber(),
        priceAllResidue: priceAllResidue.toNumber(),
      });
    }
  }

  // 结算审核 修改订单状态
  async auditingProof(orderId: number, targetStatus: number) {
    const [orderInfo] = await Promise.all([
      this.bizOrderListEntity.findOneBy({ id: orderId }),
    ]);

    if (orderInfo.status !== 4) {
      throw new CoolCommException('订单状态非结算审核中，不可更新状态！');
    }

    if (targetStatus === 3) {
      // 审核拒绝
    }
    if (targetStatus === 5) {
      // 审核通过
      // 修改排期结束日期
      await this.bizWorkerWorkDatesEntity.update(
        { orderId },
        { endDate: formatDate() }
      );
    }

    await this.bizOrderListEntity.update(
      { id: orderId },
      { status: targetStatus }
    );
  }

  // 发送短信
  async sendOrderSMS(orderNo: string) {
    const orderInfo = await this.bizOrderListEntity.findOneBy({ orderNo });
    const workerIds = orderInfo.workerIds
      .split(',')
      .map((id: string) => Number(id));

    const workers = await this.bizWorkerListEntity.findBy({
      id: In(workerIds),
    });

    const phones = workers.map(worker => worker.phone);

    await this.pluginService.invoke(
      'sms-ali',
      'send',
      phones,
      { orderNo },
      this.ctx.app.getConfig('getALiYunSMS')()
    );
    return {
      message: '发送成功',
    };
  }
}
